<?php
/***
 * Candy框架 框架URL解析类
 * 
 * $Author: 刘森 (fingerboy@qq.com) $
 * $Date: 2019年7月26日 11:35:31 $ 
 */

declare (strict_types = 1);
namespace Candy\Core;

defined('CANDY') OR die('You Are A Bad Guy. o_O???');

final Class Prourl {
	
	/**
	 *基础配置
	 */
	private static function setConfig(): void
	{
		//sprinfo
		$cainfo = [];
		if(empty($_SERVER['PATH_INFO'])){}else{
			//如果有全局路由则预处理
			$globRoute = INCPATH . 'Route.inc.php';
			$globConfig = is_file($globRoute) ? include($globRoute) : [];
			if(isset($globConfig['URL_DEPR']) && $globConfig['URL_DEPR'] == '.'){
				showTplMsg('halt', '<b>.</b>符号不能做分隔符。');
			}
			
			//保护分隔符
			$protectSeparator = isset($globConfig['PROTECT_SEP']) ? explode(',', $globConfig['PROTECT_SEP']) : [];
			$cainfo = ctypeAlnumInfo($_SERVER['PATH_INFO'], $protectSeparator);
			
			//全局路由
			if(empty($globConfig)){}else{
				//路由开启
				if($globConfig['URL_ROUTER_ON']){
					if(isset($globConfig['URL_DEPR'])){
						$pathinfo = str_replace($globConfig['URL_DEPR'], '/', $_SERVER['PATH_INFO']);
					}else{
						if(empty($cainfo)){
							$pathinfo = $_SERVER['PATH_INFO'];
						}else{
							$pathinfo = $cainfo['spr'] != '/' ? str_replace($cainfo['spr'], '/', $_SERVER['PATH_INFO']) : $_SERVER['PATH_INFO'];
						}
						
					}
					//暂时分离后缀
					$suffix = '';
					if(stripos($pathinfo, '.') != false)
						[$pathinfo, $suffix] = explode('.', $pathinfo, 2);
					
					$_SERVER['PATH_INFO'] = self::contrastRoute($pathinfo, $globConfig) . (empty($suffix) ? '' : '.' . $suffix);
					//恢复分隔符
					if(isset($globConfig['URL_DEPR']) || (isset($cainfo['spr']) && $cainfo['spr'] != '/')){
						$_SERVER['PATH_INFO'] = isset($globConfig['URL_DEPR']) ? str_replace('/', $globConfig['URL_DEPR'], $_SERVER['PATH_INFO']) : str_replace('/', $cainfo['spr'], $_SERVER['PATH_INFO']);
					}
				}
			}
		}
		
		//重置appName
		if(C('APP')){
			$appName = C('APP');
			//单例模式下
			if(empty($cainfo)){
				//常规参数或者没有参数
				$className = isset($_GET['m']) ? trim($_GET['m']) : G('default.control', '', 'Index');
			}else{
				[$className] = explode($cainfo['spr'], $_SERVER['PATH_INFO'], 2);
			}
			$defaultApp = App::getClassInfo($className, false, $appName);
			$defaultPath = CANDYROOT.'Application/'. $appName .'/Controls/'. $className .'.class.php';
			if(!is_file($defaultPath) && empty($defaultApp['extend'])){
				//不存在操作先检查是否为Addon
				$isAddon = Addon::checkAddon($className);
				if($isAddon){
					C('ISADDON', true);
					if(!empty($cainfo)){
						if($cainfo['spr'] == '.'){
							$_SERVER['PATH_INFO'] = '';
						}else{
							[$appName, $_SERVER['PATH_INFO']] = explode($cainfo['spr'], $_SERVER['PATH_INFO'], 2);
							$appName = ucfirst(strtolower($appName));
						}
					}
				}
			}
		}else{
			//支持域名绑定项目
			$bindDomainList = G('bindDomainList') ?: [];
			$bindAppList = empty($bindDomainList) ? [] : array_flip($bindDomainList);
			empty($bindAppList) OR G('bindAppList', $bindAppList);
			if(!empty($bindDomainList) && in_array($_SERVER['HTTP_HOST'], $bindDomainList)){
				//命中绑定
				$appName = $bindAppList[$_SERVER['HTTP_HOST']];
			}
			
			$s = '';
			if(empty($appName)){
				if(is_null(C('APP'))){
					if(empty($cainfo)){
						//常规传参模式
						$appName = isset($_GET['g']) ? trim($_GET['g']) : G('default.app', '', 'Home');
					}else{
						if($cainfo['spr'] == '.'){
							[$appName, $s] = explode('.', $_SERVER['PATH_INFO'], 2);
							$_SERVER['PATH_INFO'] = '';
						}else{
							[$appName, $_SERVER['PATH_INFO']] = explode($cainfo['spr'], $_SERVER['PATH_INFO'], 2);
						}
					}
				}else{
					//单例模式
					$appName = C('APP');
				}
			}
			
			//项目默认替换
			if(!is_null(G('default.reset')) && $appName == 'Home'){
				$appName = G('default.reset');
			}
			
			//多层目录检测
			$appName = ucfirst($appName);
			$tmpPath = N('Str')::snake(ucfirst($appName),'upper','/');
		}
		
		//判断是否存在 不存在则默认Home
		$appPath = CANDYROOT . 'Application/' . $tmpPath .'/';
		
		//不存在则默认Home
		if(!is_dir($appPath)){
			//默认项目操作保护
			$defaultAppName = 'Home';
			is_null(G('default.reset')) OR $defaultAppName = G('default.reset');
			$defaultApp = App::getClassInfo($appName, false, $defaultAppName);
			$defaultPath = CANDYROOT.'Application/'. $defaultAppName .'/Controls/'. $appName .'.class.php';
			if((is_file($defaultPath) || !empty($defaultApp['extend'])) && !(isDebug() && (isset($_GET['add']) && in_array($_GET['add'], ['','addon','plug'])))){
				if(is_null(C('APP'))){
					$_SERVER['PATH_INFO'] = $appName . $cainfo['spr'] . (empty($_SERVER['PATH_INFO']) ? $s : $_SERVER['PATH_INFO']);
				}
				$appName = $defaultAppName;
				$appPath = CANDYROOT . 'Application/'. $appName .'/';
			}else{
				//默认子目录是否存在
				if(is_dir(CANDYROOT . 'Application/Home/'. $appName)){
					$appPath = CANDYROOT . 'Application/Home/' . $appName .'/';
					$tmpPath = 'Home/' . $appName ;
					$appName = 'Home' . $appName;
				}else{
					//不存在操作先检查是否为Addon
					$addonName = empty($tmpPath) ? $appName : explode('/', $tmpPath)[0];
					$isAddon = Addon::checkAddon($addonName);
					if($isAddon){
						C('ISADDON', true);
					}
				}
			}
		}
		
		//绑定域名的模块机制非域名访问
		if(isset($bindDomainList[$appName]) && $bindDomainList[$appName] != $_SERVER['HTTP_HOST']){
			C('S404', '请使用正确的域名访问.'); 
		}
		
		//添加常量
		$_GET['g'] = $appName;
		$AppName = preg_replace('/(.)(?=[A-Z])/u', '$1/', $appName);
		C(['APPNAME'=>$appName,'TMPPATH'=>$AppName,'LAPPNAME'=>preg_replace('/(.)(?=[A-Z])/u', '$1\\', $appName),'RAPPNAME'=>$AppName]);
		
		//APP路径
		if(is_null(C('ISADDON'))){
			$appPath = CANDYROOT.'Application/'. C('TMPPATH') .'/';
		}else{
			$appPath = CANDYROOT.'Addons/'. C('TMPPATH') .'/';
		}
		
		//加载函数库
		if(file_exists($appPath . 'function.php'))
			requireCache($appPath . 'function.php');
		
		//保存路径
		C('APP_PATH', $appPath);
		
		//初始化
		$router = ['URL_MODEL'=>1, 'URL_DEPR'=>'/', 'URL_HTML_SUFFIX'=>'.html', 'URL_ROUTER_ON'=>false];
		
		//初始化配置
		if(function_exists('setProurlConfig')){
			$configs = setProurlConfig();
			if(is_array($configs)){
				$router = array_merge($router, $configs);
			}
		}
		
		//路由
		if(is_null(C('ISADDON'))){
			$configfile = INCPATH . $appName .'Route.inc.php';
		}else{
			$configfile = CANDYROOT.'Addons/'. $appName .'/route.php';
		}
		
		//获取配置信息
		$_config = is_file($configfile) ? include($configfile) : [];
		
		//合并配置
		$_config = array_merge($router, $_config);
		
		//存放变量
		C($_config);
		
		//补充判断
		if(!empty($s) && C('URL_HTML_SUFFIX') != '.'.$s){
			is_null(C('S404')) && C('S404', 'URL不规范,请检查URL格式.'); 
		}
	}
	
	/**
	 * 获取pathinfo
	 */
	private static function getPathInfo(): void
	{
		//初始化传参
		C([
			'METHOD'=>'GET',	//传参模式
			'S404'=>null,		//404状态
			'API'=>null,		//API状态
			'ISADDON'=>null,	//插件状态
			'ROUTINE'=>null,	//路由状态
			'ATTACK'=>null,		//攻击状态
			'APIDATA'=>null,	//API数据
			'REMOVEXSS'=>null	//POST xss
		]);
		
		//传参方式
		if($_SERVER['REQUEST_METHOD'] == 'POST'){
			C('METHOD', 'POST');
			
			//关闭xss
			if(isset($_POST['removexss'])){
				C('REMOVEXSS', true);
			}
		}
		
		//兼容workerman模式
		if(defined('CLIWORKING')){
			//PATH_INFO
			$_SERVER['PATH_INFO'] = '';
			$path_info = $parse = '';
			if(strlen($_SERVER['REQUEST_URI']) > 1 || !empty($_SERVER['QUERY_STRING']) || $_SERVER['QUERY_STRING'] == 'add'){
				list($path_info, $parse) = explode('?', $_SERVER['REQUEST_URI']);
			}
			
			if($path_info != '/' && !empty($path_info)){
				$_SERVER['PATH_INFO'] = urldecode($path_info);
			}
			
			//?以后的参数加入到$_GET
			if(!empty($parse) && $parse != 'add'){
				parse_str($parse, $get);
				$_GET = $get;
			}
		}
		
		//剔除常见css js jpg gif png ico等访问链接解析
		if(isset($_SERVER['PATH_INFO'])){
			$extra = ['.css','.jpg','.gif','.png','.ico','.mp4','.txt','404','.eot','.svg','.ttf','.woff','.woff2'];
			for($i=0;$i<count($extra);$i++){
				if(stristr($_SERVER['PATH_INFO'], $extra[$i])){
					C('S404', 'File Not Found.'); 
				}
			}
			if(stristr($_SERVER['PATH_INFO'], '.js') && !stristr($_SERVER['PATH_INFO'], '.json')){
				C('S404', 'File Not Found.'); 
			}
			
			//文件直接定义404
			if(C('S404') != null)
				headerLoading('Not Found', 404, C('S404'));
			
			if(stristr($_SERVER['PATH_INFO'], '.php')){
				$_SERVER['PATH_INFO'] = dirname($_SERVER['PATH_INFO']) . '/';
			}
		}
		
		//处理标准的PATHINFO
		if(isset($_SERVER['PATH_INFO']) && !empty($_SERVER['PATH_INFO'])){
			while(!ctype_alnum($_SERVER['PATH_INFO'][0])){
				$_SERVER['PATH_INFO'] = substr($_SERVER['PATH_INFO'],1);
			};
		}
	}
	
	/**
	 *解析URL
	 */
	public static function parseUrl(): void
	{
		//检查配置是否存在
		Build::checkInit();
		
		//获取目录
		self::getPathInfo();
		if(is_null(G('jsonrpc'))){
			self::setConfig();
			//URL模式
			$urlModel = C('URL_MODEL');
			//分隔符
			$urlDepr = C('URL_DEPR');
			//处理请求
			if(!empty($_SERVER['PATH_INFO'])){
				$get = $_GET;
				unset($get['g']);
				
				//检查suffix
				$suffix = '';
				if(stripos($_SERVER['PATH_INFO'], '.') === false){
					//除去两端的 /
					$pathinfo = trim($_SERVER['PATH_INFO'], '/');
				}else{
					$pathAgs = explode('.',$_SERVER['PATH_INFO']);
					//取出最后一个值判定是否符合suffix
					$last = array_pop($pathAgs);
					if(stripos($last, '/') === false){
						$pathinfo = str_replace('.'.$last, '',trim($_SERVER['PATH_INFO'], '/'));
						$suffix = $last ;
					}else{
						//除去两端的 /
						$pathinfo = trim($_SERVER['PATH_INFO'], '/');
					}
				}
				
				//统一模式(除去post传值情况)
				if($urlModel == 1 && count($get) > 0 && C('METHOD') == 'GET'){
					//只有add参数不跳转
					if(!(isset($_GET['add']) && (empty($_GET['add']) || $_GET['add'] = 'addon' || $_GET['add'] == 'plug'))){
						$protectArgs = is_array(G('protectArgs')) ? G('protectArgs') : [];
						foreach($get as $key=>$value){
							//跳过空值 和 受保护的值
							if(empty($value) || in_array($key, $protectArgs)){
								if(empty($value))
									unset($get[$key]);
								
								continue;	
							}else{
								unset($get[$key]);
								$pathinfo .= $urlDepr . $key . $urlDepr . $value;
							}
						}
						
						//只有保护参数不跳转
						if(count($get) != count($_GET)){
							$bindDomainList = G('bindDomainList') ?: [];
							//添加项目头
							if(C('APPNAME') != 'Home' && (empty($bindDomainList[C('APPNAME')]) || $bindDomainList[C('APPNAME')] != $_SERVER['HTTP_HOST'])){
								$pathinfo = C('APPNAME') . $urlDepr . $pathinfo;
							}
							//组个连接
							$pathinfo = '/' . $pathinfo . '.' . $suffix . (empty($get) ? '' : '?'. http_build_query($get));
							
							headerLoading('Location:'.$pathinfo);
						}
					}
				}
				
				//判断后缀
				$suffix = strtolower($suffix);
				switch($suffix){
					case 'json':
					case 'xml':
						C('API', $suffix);
						Debug::addMsg('当前：API模式('.strtoupper($suffix).'数据格式)');
						break;
					default :
						//自定义api方式
						if(function_exists('apiFunc' . ucwords($suffix))){
							C('API', $suffix);
							Debug::addMsg('当前：API模式('.strtoupper($suffix).'数据格式)');
						}elseif(substr($_SERVER['PATH_INFO'],-1) != '/' && empty($suffix)){
							headerLoading('Location:/'. ucfirst($_SERVER['PATH_INFO']) . '/');
						}elseif(!empty($suffix) && C('URL_HTML_SUFFIX') != '.'.$suffix){
							is_null(C('S404')) && C('S404', 'URL不规范,请检查URL格式.'); 
							break;
						}
						
						//正常
						$urlmsg = stripos($_SERVER['PATH_INFO'], C('URL_HTML_SUFFIX')) ? '当前：PATHINFO模式(以'. C('URL_HTML_SUFFIX') .'结尾)' : '当前：PATHINFO模式.';
						Debug::addMsg($urlmsg);
				}
				
				self::obtainParameters($urlDepr != '/' ? str_replace($urlDepr, '/',self::contrastRoute($pathinfo)) : self::contrastRoute($pathinfo));
			}else{
				//有可能的双?情况
				if(!empty($_SERVER['QUERY_STRING']) && substr_count($_SERVER['QUERY_STRING'], '?') > 0){
					parse_str(str_replace('?', '&', $_SERVER['QUERY_STRING']), $_GET);
				}
				
				//普通参数模式
				if($urlModel == 1 && !empty($_SERVER['QUERY_STRING']) && stripos($_SERVER['QUERY_STRING'], '=')){ //统一模式
					//get参数
					$get = $_GET;
					
					//项目
					if(isset($get['g'])){
						if(is_null(C('APP')) && $get['g'] != 'Home'){
							$g = ucfirst($get['g']);
						}
						unset($get['g']);  //去除数组中的g
					}
					
					//控制器和操作
					$m = ucfirst(strtolower($get['m']));
					unset($get['m']);  //去除数组中的m
					$a = strtolower($get['a']);
					unset($get['a']);  //去除数组中的a
					
					//后缀
					$suffix = C('URL_HTML_SUFFIX');
					if(isset($get['format'])){
						$suffix = '.'. strtolower($get['format']);
						unset($get['format']);
					}
					
					//保护参数
					$protectArgs = is_array(G('protectArgs')) ? G('protectArgs') : [];
					if(!empty($protectArgs)){
						$protectKey = [];
						foreach($protectArgs as $key){
							if(isset($get[$key])){
								$protectKey[$key] = $get[$key];
								unset($get[$key]);
							}
						}
					}
					
					if(count($get) > 0 || (isset($_GET['g']) || isset($_GET['m']) || isset($_GET['a']))){
						//其他参数组合
						$query = '';
						if(count($get) > 0)
							$query = http_build_query($get);
						
						//控制器组合
						$control = $g ? $g . $urlDepr : '';
						$control .= ($m ? $m : G('default.control', '', 'Index')) . $urlDepr;
						$control .= ($a ? $a : G('default.action', '', 'index')) . $urlDepr;
						//默认控制置空
						if(in_array(str_replace($urlDepr, '/', $control), ['Home/Index/index/', 'Index/index/']) && empty($query)){
							$control = '';
						}else{
							$control .= str_replace(['&', '='], $urlDepr, $query);
						}
						
						//入口文件
						$url = '/';
						if($_SERVER['SCRIPT_NAME'] != '/index.php'){
							$url= $_SERVER['SCRIPT_NAME'];
						}
						
						//组成新的URL
						$url .= empty($control) ? '' : (rtrim($control, $urlDepr) . $suffix);
						
						//保护参数
						if(!empty($protectKey)){
							$url .= '?'. http_build_query($protectKey);
						}
						headerLoading('Location:'.$url);
					}
				}else{
					if(isset($_GET['format']) && (strtolower($_GET['format'])=='json' || strtolower($_GET['format'])=='xml')){
						if(strtolower($_GET['format'])=='json'){
							C('API', 'json');
							Debug::addMsg('当前：API模式(JSON数据格式)');
						}else{
							C('API', 'xml');
							Debug::addMsg('当前：API模式(XML数据格式)');
						}
						unset($_GET['format']);
					}else{
						if(!empty($_GET))
							Debug::addMsg('当前：常规URL传参模式');
					}
					
					//引入session_id
					if(isset($_GET['sessionid'])){
						$sessionid = $_GET['sessionid'];
						unset($_GET['sessionid']);
						C('SESSID', $sessionid);
					}
				}
				
				//默认是项目名
				$_GET['g'] = is_null(C('APP')) ? ((!empty($_GET['g']) ? ucfirst($_GET['g']) : G('default.app', '', 'Home'))) : C('APP');  
				
				//默认是index模块
				$_GET['m'] = is_null(C('CONTROL')) ? (!empty($_GET['m']) ? ucfirst($_GET['m']) : G('default.control', '', 'Index')) : C('CONTROL');    
				//默认是index动作
				$_GET['a'] = is_null(C('ACTION')) ? (!empty($_GET['a']) ? $_GET['a'] : G('default.action', '', 'index')) : C('ACTION');
			}
		}else{
			//server 模式
			$serverParams = G('jsonrpc');
			
			//传递参数
			if(isset($serverParams['post'])) {
				$_POST = $serverParams['post'];
				unset($serverParams['post']);
			}
			$_GET = $serverParams;
			
			//关闭调试
			debug(0);
			
			//重置
			G('jsonrpc', null);
		}
		
		//运行获取参数后中间件
		Security::runMiddlewares('After');
		
		//定义控制器和操作
		C('CONTROL', $_GET['m']);
		C('ACTION', $_GET['a']);
	}
	
	/**
	 *处理路由规则
	 */
	private static function contrastRoute(string $pathinfo,array $config = []): string
	{
		//预置路由之前处理方法
		if(function_exists('beforeContrastRoute')){
			$pathinfo = beforeContrastRoute($pathinfo);
		}
		
		// URL映射定义（静态路由）
		$maps = empty($config) ? C('URL_MAP_RULES') : $config['URL_MAP_RULES'];
		if(isset($maps[$pathinfo])){
			return is_string($maps[$pathinfo]) ? $maps[$pathinfo] : '';
		}
		
		// 动态路由处理
		$routes = empty($config) ? C('URL_ROUTE_RULES') : $config['URL_ROUTE_RULES'];
		if(!empty($routes)){
			foreach($routes as $rule=>$route){
				if(is_numeric($rule)){
					// 支持 array('rule','adddress',...) 定义路由
					$rule = array_shift($route);
				}
				if(is_array($route) && isset($route[2])){
					// 路由参数
					$options = $route[2];
					if(isset($options['ext']) && C('URL_HTML_SUFFIX') != $options['ext']){
						// URL后缀检测
						continue;
					}
					if(isset($options['method']) && C('METHOD') != strtoupper($options['method'])){
						// 请求类型检测
						continue;
					}
					// 自定义检测
					if(!empty($options['callback']) && is_callable($options['callback'])){
						if(false === call_user_func($options['callback'])){
							continue;
						}
					}                    
				}
				if(0 === strpos($rule,'/') && preg_match($rule, $pathinfo, $matches)){ // 正则路由
					if($route instanceof \Closure){
						// 执行闭包
						$return = self::invokeRegx($route, $matches);
						return is_string($return) ? $return : '';
					}else{
						$return = self::parseRegex($matches,$route,$pathinfo);
						return is_string($return) ? $return : '';
					}
				}else{
					// 规则路由
					$len1 = substr_count($pathinfo,'/');
					$len2 = substr_count($rule,'/');
					if($len1>=$len2 || strpos($rule,'[')){
						if('$' == substr($rule,-1,1)){// 完整匹配
							if($len1 != $len2){
								continue;
							}else{
								$rule = substr($rule,0,-1);
							}
						}
						$match = self::checkUrlMatch($pathinfo,$rule);
						if(false !== $match){
							if($route instanceof \Closure){
								// 执行闭包
								$return = self::invokeRule($route, $match);
								return is_string($return) ? $return : '';
							}else{
								$return = self::parseRule($rule,$route,$pathinfo);
								return is_string($return) ? $return : '';
							}
						}
					}else{
						return empty($pathinfo) ? '' : $pathinfo;
					}
				}
			}
		}
		return empty($pathinfo) ? '' : $pathinfo;
	}
	
	/**
	 *PATHINFO解析为$_GET参数
	 */
	private static function obtainParameters(string $pathInfoStr): void
	{
		$pathinfo = explode('/', $pathInfoStr);
		
		//去除第一个带点的元素 避免类似于 /Favicon.ico  /style.css
		!stripos($pathinfo[0], '.') OR array_shift($pathinfo);
		
		// 处理函数
		foreach($pathinfo as $key=>$val){
			if(strpos($val,'|')){
				list($val,$fun) = explode('|',$val);
				$pathinfo[$key] = $fun($val);
			}
		}
		
		// 获取 control  修复伪静态目录添加默认文档
		$_GET['g'] = is_null(C('APP')) ? ((!empty($_GET['g']) ? ucfirst($_GET['g']) : G('default.app', '', 'Home'))) : C('APP');
		
		// app
		if(is_null(C('CONTROL'))){
			$_GET['m'] = !empty($pathinfo[0]) ? (stristr($pathinfo[0], '.php') ? G('default.control', '', 'Index') : ucfirst($pathinfo[0])) : G('default.control', '', 'Index');
			array_shift($pathinfo); //将数组开头的单元移出数组 
		}else{
			$_GET['m'] = C('CONTROL');
		}
		
		// 获取 action 修复伪静态目录添加默认文档
		if(is_null(C('ACTION'))){
			$_GET['a'] = !empty($pathinfo[0]) ? (stristr($pathinfo[0], '.php') ? G('default.action', '', 'index') : $pathinfo[0]) : G('default.action', '', 'index');
			array_shift($pathinfo); //再将将数组开头的单元移出数组
		}else{
			$_GET['a'] = C('ACTION');
		}
		
		for($i=0; $i<count($pathinfo); $i+=2){
			$_GET[$pathinfo[$i]]=$pathinfo[$i+1];
		}
	}
	
	/**
	 *检测URL和规则路由是否匹配
	 */
	private static function checkUrlMatch(string $regx,string $rule){
		$m1 = explode('/',$regx);
		$m2 = explode('/',$rule);
		$var = [];         
		foreach($m2 as $key=>$val){
			if(0 === strpos($val,'[:')){
				$val = substr($val,1,-1);
			}
				
			if(':' == substr($val,0,1)){// 动态变量
				if($pos = strpos($val,'|')){
					// 使用函数过滤
					$val = substr($val,1,$pos-1);
				}
				if(strpos($val,'\\')){
					$type = substr($val,-1);
					if('d'==$type){
						if(isset($m1[$key]) && !is_numeric($m1[$key]))
							return false;
					}
					$name = substr($val, 1, -2);
				}elseif($pos = strpos($val,'^')){
					$array = explode('-',substr(strstr($val,'^'),1));
					if(in_array($m1[$key],$array)){
						return false;
					}
					$name = substr($val, 1, $pos - 1);
				}else{
					$name = substr($val, 1);
				}
				$var[$name] = isset($m1[$key])?$m1[$key]:'';
			}elseif(0 !== strcasecmp($val,$m1[$key])){
				return false;
			}
		}
		// 成功匹配后返回URL中的动态变量数组
		return $var;
	}
	
	/**
	 *解析规则路由
	 *
	 * '路由规则'=>'[控制器/操作]?额外参数1=值1&额外参数2=值2...'
	 * '路由规则'=>array('[控制器/操作]','额外参数1=值1&额外参数2=值2...')
	 * '路由规则'=>'外部地址'
	 * '路由规则'=>array('外部地址','重定向代码')
	 * 路由规则中 :开头 表示动态变量
	 * 外部地址中可以用动态变量 采用 :1 :2 的方式
	 * 'news/:month/:day/:id'=>array('News/read?cate=1','status=1'),
	 * 'new/:id'=>array('/new.php?id=:1',301), 重定向
	 */
	private static function parseRule(string $rule, $route,string $regx): string
	{
		// 获取路由地址规则
		$url = is_array($route) ? $route[0] : $route;
		// 获取URL地址中的参数
		$paths = explode('/',$regx);
		// 解析路由规则
		$matches = [];
		$rule = explode('/',$rule);
		foreach($rule as $item){
			$fun  = '';
			if(0 === strpos($item,'[:')){
				$item = substr($item,1,-1);
			}
			if(0 === strpos($item,':')){ // 动态变量获取
				if($pos = strpos($item,'|')){ 
					// 支持函数过滤
					$fun = substr($item,$pos+1);
					$item = substr($item,0,$pos);
				}
				if($pos = strpos($item,'^') ){
					$var = substr($item,1,$pos-1);
				}elseif(strpos($item,'\\')){
					$var = substr($item,1,-2);
				}else{
					$var = substr($item,1);
				}
				
				$matches[$var] = !empty($fun)? $fun(array_shift($paths)) : array_shift($paths);
			}else{ // 过滤URL中的静态变量
				array_shift($paths);
			}
		}
		if(0 === strpos($url,'/') || 0 === strpos($url,'http')){ // 路由重定向跳转
			if(strpos($url,':')){ // 传递动态参数
				$values = array_values($matches);
				$url = preg_replace_callback('/:(\d+)/', function($match) use($values){ return $values[$match[1] - 1]; }, $url);
			}
			headerLoading("Location: $url");
			stop();
		}else{
			// 解析路由地址里面的动态参数
			$var = [];
			$values = array_values($matches);
			foreach($var as $key=>$val){
				if(0 === strpos($val,':')){
					$var[$key] = $values[substr($val,1)-1];
				}
			}
			$var = array_merge($matches,$var);
			// 解析剩余的URL参数
			if(!empty($paths)){
				preg_replace_callback('/(\w+)\/([^\/]+)/', function($match) use(&$var){ $var[strtolower($match[1])]=strip_tags($match[2]);}, implode('/',$paths));
			}
			// 解析路由自动传入参数
			if(is_array($route) && isset($route[1])){
				if(is_array($route[1])){
					$params = $route[1];
				}else{
					parse_str($route[1],$params);
				}      
				$var = array_merge($var,$params);
			}
			//拼接参数
			$varStr = '';
			if(!empty($var)){
				$varStr = '/';
				foreach($var as $key=>$value){
					$varStr .= $key .'/' . $value . '/';
				}
				$varStr = rtrim($varStr, '/');
			}
			return $url . $varStr;
		}
	}
	
	/**
	 *解析规则路由
	 *
	 * 解析正则路由
	 * '路由正则'=>'[控制器/操作]?参数1=值1&参数2=值2...
	 * '路由正则'=>array('[控制器/操作]?参数1=值1&参数2=值2...','额外参数1=值1&额外参数2=值2...')
	 * '路由正则'=>'外部地址'
	 * '路由正则'=>array('外部地址','重定向代码')
	 * 参数值和外部地址中可以用动态变量 采用 :1 :2 的方式
	 * '/new\/(\d+)\/(\d+)/'=>array('News/read?id=:1&page=:2&cate=1','status=1'),
	 * '/new\/(\d+)/'=>array('/new.php?id=:1&page=:2&status=1','301'), 重定向
	 */
	private static function parseRegex(array $matches, $route,string $regx): string
	{
		// 获取路由地址规则
		$url = is_array($route) ? $route[0] : $route;
		$url = preg_replace_callback('/:(\d+)/', function($match) use($matches){return $matches[$match[1]];}, $url); 
		if(0 === strpos($url,'/') || 0 === strpos($url,'http')){ // 路由重定向跳转
			headerLoading("Location: $url");
			stop();
		}else{
			$var = [];
			// 解析剩余的URL参数
			$regx = substr_replace($regx,'',0,strlen($matches[0]));
			if($regx){
				preg_replace_callback('/(\w+)\/([^\/]+)/', function($match) use(&$var){
					$var[strtolower($match[1])] = strip_tags($match[2]);
				}, $regx);
			}
			// 解析路由自动传入参数
			if(is_array($route) && isset($route[1])){
				if(is_array($route[1])){
					$params = $route[1];
				}else{
					parse_str($route[1],$params);
				}
				$var = array_merge($var,$params);
			}
			//拼接参数
			$varStr = '';
			if(!empty($var)){
				$varStr = '/';
				foreach($var as $key=>$value){
					$varStr .= $key .'/' . $value . '/';
				}
				$varStr = rtrim($varStr, '/');
			}
			return $url . $varStr;
		}
	}
	
	/**
	 *执行正则匹配下的闭包方法 支持参数调用
	 */
	private static function invokeRegx($closure,array $var = []):mixed
	{
		$reflect = new \ReflectionFunction($closure);
		$params  = $reflect->getParameters();
		$args  = [];
		array_shift($var);
		foreach($params as $param){
			if(!empty($var)){
				$args[] = array_shift($var);
			}elseif($param->isDefaultValueAvailable()){
				$args[] = $param->getDefaultValue();
			}
		}
		return $reflect->invokeArgs($args);
	}
	
	/**
	 *执行规则匹配下的闭包方法 支持参数调用
	 */
	private static function invokeRule($closure,array $var = []):mixed
	{
		$reflect = new \ReflectionFunction($closure);
		$params  = $reflect->getParameters();
		$args  = [];
		foreach($params as $param){
			$name = $param->getName();
			if(isset($var[$name])){
				$args[] = $var[$name];
			}elseif($param->isDefaultValueAvailable()){
				$args[] = $param->getDefaultValue();
			}
		}
		return $reflect->invokeArgs($args);
	}
}
